식별할 수 있는 문자열(문자열 리터럴) 확인하는 방법


99_1.png

string extends T를 추가로 검사하면

string은 문자열 리터럴 타입을 상속하지 않으므로

string과 문자열 리터럴 타입을 구분할 수 있게 된다.

이것을 활용하여 다음과 같은 유틸리티를 만들었다.

import { generatePath } from 'react-router'
import type { ParamParseKey } from 'react-router/lib/router'

import type { IdentifiableString } from '~/types/utilityTypes'

// ParamParseKey는 :로 시작하는 동적 라우트 키 값의 이름을 뽑아온다.
// ParamParseKey<"/:userName/aaa/:blogId/edit"> -> 'userName' | 'blogId'

export const routeMatch = {
  login: '/login',

  categoryList: '/:userName/category',
  category: '/:userName/category/:categoryId',
  categoryPostList: '/:userName/category/:categoryId/list/:postId',

  newPost: '/:userName/post/new',
  editPost: '/:userName/post/:postId/edit',
} as const

type RouterPathFn<T extends string> = (
  ...params: IdentifiableString<ParamParseKey<T>> extends true
    ? [
        {
          [key in ParamParseKey<T>]: string
        }
      ]
    : []
) => string

const makePathFunc =
  <Path extends string>(patternPath: Path): RouterPathFn<Path> =>
  (...params) =>
    generatePath(patternPath, ...params)

type RouteMatch = typeof routeMatch
type PathNames = keyof RouteMatch

export const routerPaths = Object.fromEntries(
  Object.entries(routeMatch).map(
    ([pathName, path], cv) => [pathName, makePathFunc(path)]
  )
) as {
  [key in PathNames]: RouterPathFn<RouteMatch[key]>
}
const categoryListPath = routerPaths.categoryList({ userName: 'gururu' })
// => '/gururu/category'

이러면 routeMatch에 경로에 해당하는 문자열만 추가해주면 알아서 routerPaths에 해당하는 generator 함수가 만들어진다.